{
 "cells": [
  {
   "cell_type": "markdown",
   "source": [
    "# 02. langchain的快速入门\n",
    "https://python.langchain.com/docs/get_started/quickstart"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "from langchain.llms import OpenAI\n",
    "\n",
    "# llm = OpenAI(openai_api_key=\"...\") # 如果你有openai的api key，可以在这里填入\n",
    "llm = OpenAI() # 这种写法是通过系统环境变量获取openai的api key"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "is_executing": true
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "#### 环境变量设置\n",
    "第一步，按下“win+r”键，打开运行框，\n",
    "\n",
    "第二步，输入命令“control system”,\n",
    "\n",
    "第三步，在控制面板主页，打开“高级系统设置”，\n",
    "\n",
    "第四步，在高级系统设置界面，选择“环境变量”选项，\n",
    "\n",
    "第五步，根据需求进行设置。"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "outputs": [],
   "source": [
    "#设置代理\n",
    "import os\n",
    "\n",
    "os.environ['http_proxy'] = 'http://127.0.0.1:10809'\n",
    "os.environ['https_proxy'] = 'http://127.0.0.1:10809'\n"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "start_time": "2023-08-06T12:05:33.963902Z",
     "end_time": "2023-08-06T12:05:33.971900Z"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "## 2.1. 构建应用程序\n",
    "现在我们可以开始构建语言模型应用程序了。LangChain提供了许多可用于构建语言模型应用程序的模块。模块可以在简单的应用程序中作为独立模块使用，并且可以组合用于更复杂的用例。在本节中，我们将介绍如何使用LangChain的模块来构建一个简单的应用程序。\n",
    "\n",
    "LangChain应用程序的核心构建块是LLMChain。这结合了三件事：\n",
    "\n",
    "    1.LLM：语言模型是这里的核心推理引擎。为了使用 LangChain，您需要了解不同类型的语言模型以及如何使用它们。\n",
    "\n",
    "    2.提示模板：这为语言模型提供说明。这控制语言模型输出的内容，因此了解如何构造提示和不同的提示策略至关重要。\n",
    "\n",
    "    3.输出解析器：这些将来自LLM的原始响应转换为更可行的格式，从而可以轻松使用下游的输出。\n",
    "\n",
    "我们将单独介绍这三个组件，然后介绍组合所有这些组件的LLMChain。\n",
    "\n",
    "了解这些概念将为您能够使用和自定义 LangChain 应用程序做好准备。\n",
    "\n",
    "大多数LangChain应用程序都允许您配置LLM和/或使用的提示，因此知道如何利用这一点将是一个很大的推动因素。"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 2.1.1. LLMs\n",
    "有两种类型的语言模型，在LangChain中称为：\n",
    "```\n",
    "llms：这是一个语言模型，它将字符串作为输入并返回一个字符串\n",
    "chat_models：这是一种语言模型，它将消息列表作为输入并返回消息\n",
    "```\n",
    "\n",
    "LLM 的输入/输出简单易懂 - 一个字符串。\n",
    "\n",
    "但是chat_models呢？\n",
    "\n",
    "那里的输入是ChatMessage的列表 ，输出是单个ChatMessage .\n",
    "\n",
    "一个ChatMessage有两个必需的组件：\n",
    "```\n",
    "content ：这是消息的内容。\n",
    "role ：这是来自的 ChatMessage 实体的角色。\n",
    "```\n",
    "LangChain 提供了几个对象来轻松区分不同的角色：\n",
    "```\n",
    "HumanMessage ： ChatMessage来自人类/用户。\n",
    "\n",
    "AIMessage ： ChatMessage来自AI/助手。\n",
    "\n",
    "SystemMessage ：来自系统的ChatMessage 。\n",
    "\n",
    "FunctionMessage ：来自函数调用的ChatMessage 。\n",
    "```"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "LangChain 为两者公开了一个标准接口，但了解这种差异以便为给定语言模型构造提示很有用。LangChain 公开的标准接口有两种方法：\n",
    "\n",
    "predict ：接受字符串，返回字符串\n",
    "\n",
    "predict_messages ：接收消息列表，返回消息。"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "outputs": [],
   "source": [
    "from langchain.llms import OpenAI\n",
    "from langchain.chat_models import ChatOpenAI\n",
    "\n",
    "llm = OpenAI()\n",
    "chat_model = ChatOpenAI()"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "start_time": "2023-08-06T15:51:07.050846Z",
     "end_time": "2023-08-06T15:51:07.077669Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "outputs": [
    {
     "data": {
      "text/plain": "'Hello! How can I assist you today?'"
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chat_model.predict(\"hi!\")"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "start_time": "2023-08-06T15:51:58.253136Z",
     "end_time": "2023-08-06T15:52:00.433996Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "outputs": [
    {
     "data": {
      "text/plain": "'\\n\\nHola!\\n\\n¿Cómo estás?\\n\\nEstoy bien, gracias. ¿Y tú?'"
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "llm.predict(\"hi!\")"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "start_time": "2023-08-06T15:52:00.782208Z",
     "end_time": "2023-08-06T15:52:01.928783Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "outputs": [
    {
     "data": {
      "text/plain": "'\\n\\nBrightSox.'"
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "text = \"What would be a good company name for a company that makes colorful socks?\"\n",
    "\n",
    "llm.predict(text)\n",
    "# >> Feetful of Fun"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "start_time": "2023-08-06T15:52:47.426474Z",
     "end_time": "2023-08-06T15:52:49.092752Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "outputs": [
    {
     "data": {
      "text/plain": "'Vibrant Sox'"
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chat_model.predict(text)\n",
    "# >> Socks O'Color"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "start_time": "2023-08-06T15:52:57.503066Z",
     "end_time": "2023-08-06T15:52:58.396183Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "outputs": [],
   "source": [
    "from langchain.schema import HumanMessage\n",
    "\n",
    "text = \"What would be a good company name for a company that makes colorful socks?\"\n",
    "messages = [HumanMessage(content=text)]"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "start_time": "2023-08-06T12:14:31.467790Z",
     "end_time": "2023-08-06T12:14:31.483788Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "outputs": [
    {
     "data": {
      "text/plain": "AIMessage(content='Rainbow Threads', additional_kwargs={}, example=False)"
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chat_model.predict_messages(messages)\n",
    "# >> Socks O'Color"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "start_time": "2023-08-06T12:14:32.892817Z",
     "end_time": "2023-08-06T12:14:33.690209Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "outputs": [
    {
     "data": {
      "text/plain": "AIMessage(content='\\n\\nSocktacular!', additional_kwargs={}, example=False)"
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "llm.predict_messages(messages)\n",
    "# >> Feetful of Fun"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "start_time": "2023-08-06T12:14:37.786630Z",
     "end_time": "2023-08-06T12:14:39.849929Z"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "对于这两种方法，还可以将参数作为关键字参数传入。\n",
    "\n",
    "例如，您可以传入 temperature=0 以根据对象的配置调整使用的温度。在运行时传入的任何值都将始终覆盖对象配置的内容。"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "outputs": [
    {
     "data": {
      "text/plain": "AIMessage(content='VibrantSox', additional_kwargs={}, example=False)"
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chat_model.predict_messages(messages,temperature=0)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "start_time": "2023-08-06T12:16:50.746953Z",
     "end_time": "2023-08-06T12:16:52.085650Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "outputs": [
    {
     "data": {
      "text/plain": "AIMessage(content='Socktastic', additional_kwargs={}, example=False)"
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chat_model.predict_messages(messages,temperature=0.5)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "start_time": "2023-08-06T12:16:58.280418Z",
     "end_time": "2023-08-06T12:16:58.988274Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "outputs": [
    {
     "data": {
      "text/plain": "AIMessage(content='VividSocks', additional_kwargs={}, example=False)"
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chat_model.predict_messages(messages,temperature=1)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "start_time": "2023-08-06T12:17:04.429252Z",
     "end_time": "2023-08-06T12:17:05.186200Z"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 2.1.2. 提示模板\n",
    "大多数LLM应用程序不会将用户输入直接传递到LLM。\n",
    "通常，他们会将用户输入添加到称为提示模板的较大文本中，该文本为手头的特定任务提供额外的上下文。"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "outputs": [
    {
     "data": {
      "text/plain": "'What is a good name for a company that makes colorful socks?'"
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.prompts import PromptTemplate\n",
    "\n",
    "prompt = PromptTemplate.from_template(\"What is a good name for a company that makes {product}?\")\n",
    "prompt.format(product=\"colorful socks\")"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "start_time": "2023-08-06T12:22:05.326533Z",
     "end_time": "2023-08-06T12:22:05.356534Z"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "提示模板还可用于生成消息列表。在这种情况下，提示不仅包含有关内容的信息，还包含每条消息（其角色，其在列表中的位置等）在这里，最常发生的是聊天提示模板是聊天消息模板的列表。\n",
    "每个聊天消息模板都包含有关如何格式化该聊天消息的说明 - 它的角色，然后是它的内容。"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "outputs": [
    {
     "data": {
      "text/plain": "[SystemMessage(content='You are a helpful assistant that translates English to chinese.', additional_kwargs={}),\n HumanMessage(content='I love programming.', additional_kwargs={}, example=False)]"
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.prompts.chat import (\n",
    "    ChatPromptTemplate,\n",
    "    SystemMessagePromptTemplate,\n",
    "    HumanMessagePromptTemplate,\n",
    ")\n",
    "\n",
    "template = \"You are a helpful assistant that translates {input_language} to {output_language}.\"\n",
    "system_message_prompt = SystemMessagePromptTemplate.from_template(template)\n",
    "human_template = \"{text}\"\n",
    "human_message_prompt = HumanMessagePromptTemplate.from_template(human_template)\n",
    "\n",
    "chat_prompt = ChatPromptTemplate.from_messages([system_message_prompt, human_message_prompt])\n",
    "\n",
    "chat_prompt = chat_prompt.format_messages(input_language=\"English\", output_language=\"chinese\", text=\"I love programming.\")\n",
    "\n",
    "chat_prompt"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "start_time": "2023-08-06T12:23:01.940376Z",
     "end_time": "2023-08-06T12:23:01.981472Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "outputs": [
    {
     "data": {
      "text/plain": "AIMessage(content='\\n\\n系統：我愛編程。', additional_kwargs={}, example=False)"
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "llm.predict_messages(chat_prompt)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "start_time": "2023-08-06T12:23:18.308241Z",
     "end_time": "2023-08-06T12:23:22.292505Z"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 2.1.3. 输出解析器\n",
    "输出解析器将LLM的原始输出转换为可以在下游使用的格式。输出分析器的主要类型很少，包括：\n",
    "\n",
    "\n",
    "从LLM转换文本->结构化信息（例如JSON）\n",
    "\n",
    "将聊天消息转换为字符串\n",
    "\n",
    "将调用返回的额外信息转换为字符串，而不是消息（如 OpenAI 函数调用）。"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "outputs": [
    {
     "data": {
      "text/plain": "['hi', 'bye']"
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.schema import BaseOutputParser\n",
    "\n",
    "class CommaSeparatedListOutputParser(BaseOutputParser):\n",
    "    \"\"\"Parse the output of an LLM call to a comma-separated list.\"\"\"\n",
    "\n",
    "\n",
    "    def parse(self, text: str):\n",
    "        \"\"\"Parse the output of an LLM call.\"\"\"\n",
    "        return text.strip().split(\", \")\n",
    "\n",
    "CommaSeparatedListOutputParser().parse(\"hi, bye\")\n"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "start_time": "2023-08-06T12:26:08.620537Z",
     "end_time": "2023-08-06T12:26:08.688533Z"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 2.1.4. 大语言模型链\n",
    "我们现在可以将所有这些组合成一个链。此链将获取输入变量，将这些变量传递给提示模板以创建提示，将提示传递给 LLM，然后通过（可选）输出分析器传递输出。这是捆绑模块化逻辑的便捷方法。让我们看看它的实际效果！"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "outputs": [
    {
     "data": {
      "text/plain": "['red', 'blue', 'green', 'yellow', 'orange']"
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.chat_models import ChatOpenAI\n",
    "from langchain.prompts.chat import (\n",
    "    ChatPromptTemplate,\n",
    "    SystemMessagePromptTemplate,\n",
    "    HumanMessagePromptTemplate,\n",
    ")\n",
    "from langchain.chains import LLMChain\n",
    "from langchain.schema import BaseOutputParser\n",
    "\n",
    "class CommaSeparatedListOutputParser(BaseOutputParser):\n",
    "    \"\"\"Parse the output of an LLM call to a comma-separated list.\"\"\"\n",
    "\n",
    "\n",
    "    def parse(self, text: str):\n",
    "        \"\"\"Parse the output of an LLM call.\"\"\"\n",
    "        return text.strip().split(\", \")\n",
    "\n",
    "template = \"\"\"You are a helpful assistant who generates comma separated lists.\n",
    "A user will pass in a category, and you should generate 5 objects in that category in a comma separated list.\n",
    "ONLY return a comma separated list, and nothing more.\"\"\"\n",
    "system_message_prompt = SystemMessagePromptTemplate.from_template(template)\n",
    "human_template = \"{text}\"\n",
    "human_message_prompt = HumanMessagePromptTemplate.from_template(human_template)\n",
    "\n",
    "chat_prompt = ChatPromptTemplate.from_messages([system_message_prompt, human_message_prompt])\n",
    "chain = LLMChain(\n",
    "    llm=ChatOpenAI(),\n",
    "    prompt=chat_prompt,\n",
    "    output_parser=CommaSeparatedListOutputParser()\n",
    ")\n",
    "chain.run(\"colors\")\n",
    "# >> ['red', 'blue', 'green', 'yellow', 'orange']"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "start_time": "2023-08-06T12:29:56.306080Z",
     "end_time": "2023-08-06T12:29:57.806785Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [],
   "metadata": {
    "collapsed": false
   }
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
